#include "uds.h"
#include <sys/socket.h>
#include <netinet/in.h>
#include <sys/un.h>
#include <errno.h>
#include <stdlib.h>
#include <stdio.h>

#include "cJSON_ext.h"
#include "cJSON.h"


#ifndef UDS_DBG
#define UDS_DBG(format, argument...)    do{ printf("[ %s ]  %s (line %d) -> " format"\r\n", __FUNCTION__,  __FILE__ ,__LINE__, ##argument ); }while(0)
#endif


static void udsmutex_lock(pt_uds_handle_arg p_handle)
{
    md_thread_mutex_lock(&p_handle->mutex);
}

static void udsmutex_unlock(pt_uds_handle_arg p_handle)
{
    md_thread_mutex_unlock(&p_handle->mutex);
}

int uds_init(pt_uds_handle_arg p_handle)
{
    int ierro = -1;
	uint32_t  size = 0;
	socklen_t optlen = sizeof(size);
    struct sockaddr_un sock_addr;
    if(NULL == p_handle)
    {
        UDS_DBG("handle is null");
        return ierro;
    }

    if(NULL == p_handle->local_sock_name )
    {
        UDS_DBG("local_sock_name fail");
        return ierro;
    }
    unlink(p_handle->local_sock_name);	                //解除原有socket对象链接
	p_handle->sock_fd = socket(AF_UNIX, SOCK_DGRAM, 0); //创建socket，指定通信协议为AF_UNIX,数据方式SOCK_DGRAM
    if(p_handle->sock_fd < 0)
    {
        UDS_DBG("sock_fd create fail %d",p_handle->sock_fd);
        return ierro;
    }
#if 0
    if(getsockopt(p_handle->sock_fd, SOL_SOCKET , SO_SNDBUF , &size , &optlen)){
		UDS_DBG("uds获取发送缓冲区大小错误!!!\n");   
    }
	else{
		UDS_DBG("uds发送缓冲区原始大小为:%ld \n" , size);
	}
    if(getsockopt(p_handle->sock_fd , SOL_SOCKET , SO_RCVBUF , &size , &optlen)){
        UDS_DBG("uds获取接收缓冲区大小错误!!!\n");
    }
	else{
		UDS_DBG("uds接收缓冲区原始大小为:%ld \n" , size);
	}
#endif
    size = MTU_MAX; 
    if(setsockopt(p_handle->sock_fd , SOL_SOCKET , SO_SNDBUF , &size , optlen)){
       UDS_DBG("设置发送缓冲区大小错误!!!\n");  
    }
    size = MTU_MAX; 
    if(setsockopt(p_handle->sock_fd , SOL_SOCKET , SO_RCVBUF , &size , optlen)){
       UDS_DBG("设置接收缓冲区大小错误!!!\n");  
    } 
#if 0
	if(getsockopt(p_handle->sock_fd, SOL_SOCKET , SO_SNDBUF , &size , &optlen)){
		UDS_DBG("uds获取发送缓冲区大小错误!!!\n");	
	}
	else{
		UDS_DBG("uds发送缓冲区原始大小为:%ld \n" , size);
	}
	if(getsockopt(p_handle->sock_fd , SOL_SOCKET , SO_RCVBUF , &size , &optlen)){
		UDS_DBG("uds获取接收缓冲区大小错误!!!\n");
	}
	else{
		UDS_DBG("uds接收缓冲区原始大小为:%ld \n" , size);
	}
#endif

    memset(&sock_addr, 0, sizeof(struct sockaddr_un));
	sock_addr.sun_family = AF_UNIX;
	strncpy(sock_addr.sun_path, p_handle->local_sock_name,sizeof(sock_addr.sun_path) - 1);
    ierro = bind(p_handle->sock_fd, (struct sockaddr *)&sock_addr, sizeof(struct sockaddr_un));
    if(ierro < 0)
    {
        UDS_DBG("sock_fd bind fail %d",ierro);
        return ierro;
    }

	p_handle->send_id = 0;
	md_thread_mutex_init(&p_handle->mutex,(char*)"uds");
	
    memset(&p_handle->uds_fp_tx_list,0x00,sizeof(t_uds_fp_tx));
    memset(&p_handle->uds_fp_listen_list,0x00,sizeof(t_uds_fp_listen));
    INIT_LIST_HEAD(&p_handle->uds_fp_tx_list.list);
    INIT_LIST_HEAD(&p_handle->uds_fp_listen_list.list);
	
 	UDS_DBG("uds init .");
    return p_handle->sock_fd;
}

int uds_task(pt_uds_handle_arg p_handle)
{
    int ret = 0; 
    struct timeval tv;
    fd_set readfds;
    char *buff = malloc(MTU_MAX);
    if(buff == NULL)
    {
        UDS_DBG("buff malloc fail");
        return -1;
    }
    int64_t buff_len = 0;
    memset(buff,0x00,MTU_MAX);
    if(p_handle->sock_fd <= 0 || NULL == p_handle)
    {
        UDS_DBG("sock_fd fail");
        return -1;
    }

    while(1)
    {
        FD_ZERO(&readfds);
        FD_SET(p_handle->sock_fd,&readfds);
        tv.tv_sec = 0;
        tv.tv_usec = 1000 * 20;  //20ms
        ret = select(p_handle->sock_fd + 1,&readfds,NULL,NULL,&tv);
        if(ret == 0) //timeout
        {
        	udsmutex_lock(p_handle);
            tx_data_ack_timeout_handle(&p_handle->uds_fp_tx_list.list);
			udsmutex_unlock(p_handle);
        }
        else{
            if(FD_ISSET(p_handle->sock_fd,&readfds))
            {
                if((buff_len = recv(p_handle->sock_fd,buff,MTU_MAX,0)) >= 0)
                {    
                	udsmutex_lock(p_handle);
                    rx_data_decode_handle(buff,buff_len,&p_handle->uds_fp_tx_list.list,&p_handle->uds_fp_listen_list.list);
					udsmutex_unlock(p_handle);
                }
            }
        }
    }
}

static int uds_data_sendto(pt_uds_handle_arg p_handle,uint8_t *pbuff,int64_t len,char *sock_path)
{
    struct sockaddr_un svaddr;
    int ierro = -1;
    if(p_handle <= 0 || NULL == sock_path)
    {
        UDS_DBG("sock_fd fail || sock_path is null");
        return -1;
    }
    memset(&svaddr, 0, sizeof(struct sockaddr_un));
    svaddr.sun_family = AF_UNIX;
    strncpy(svaddr.sun_path, sock_path, sizeof(svaddr.sun_path) - 1);

#if 0
	t_frame *frame = (t_frame *)pbuff;
	printf("send %s\r\n",frame->payload);
#endif

    ierro = sendto(p_handle->sock_fd, pbuff, len, 0, (struct sockaddr *) &svaddr, sizeof(struct sockaddr_un));

    return ierro;
}

int uds_topic_notify_ex(pt_uds_handle_arg p_handle, cJSON *notify, tx_callback_func cb, uint32_t outtime )
{
	pt_uds_fp_tx tmp = NULL;
	char *data = NULL;
	int res = -1;
	if(!p_handle || !notify ){
		return -1;
	}
	udsmutex_lock(p_handle);
	p_handle->send_id++;
	cJSON_AddNumberToObject(notify, "id", p_handle->send_id);
	
	data = cJSON_PrintUnformatted(notify);
	tmp = tx_msg_create_handle(&p_handle->uds_fp_tx_list,
								data,
								strlen(data),
								outtime,
								cb);
	free(data);
	if(!tmp){
		UDS_DBG("uds_topic_notify_ex create fail.");
		goto EXIT;
	}
	cJSON *frame_dst = cJSON_GetObjectItem(notify, "to");
	if(frame_dst != NULL && frame_dst->valuestring != NULL ){
		//run send
		res = uds_data_sendto(p_handle,(uint8_t *)tmp->pframe,(tmp->pframe->payload_len + sizeof(t_frame)),frame_dst->valuestring);
	}
	if(res < 0){
		UDS_DBG("uds_data_sendto fail.");
	}
EXIT:
	udsmutex_unlock(p_handle);
	return res;
}

int uds_topic_ack_ex(pt_uds_handle_arg p_handle, cJSON *ack)
{
	int res = -1;
	t_frame *tmp = NULL;
	char *data = NULL;
	if(!p_handle || !ack ){
		return -1;
	}
	data = cJSON_PrintUnformatted(ack);
	tmp = ack_msg_create_handle(data,strlen(data));
	free(data);
	cJSON *frame_dst = cJSON_GetObjectItem(ack, "to");
	if(frame_dst != NULL && frame_dst->valuestring != NULL ){
		//run send
		res = uds_data_sendto(p_handle,(uint8_t *)tmp,(tmp->payload_len + sizeof(t_frame)),frame_dst->valuestring);
	}
	if(res < 0){
		UDS_DBG("uds_data_sendto fail.");
	}
	ack_msg_free_handle(tmp);
	return res;
}

//监听消息：如果传入src，则需要同时匹配topic和src才能命中消息，src为NULL，则不匹配src
int uds_topic_listen_ex(pt_uds_handle_arg p_handle,char *topic,char *src,listen_callback_func cb)
{
	pt_uds_fp_listen tmp = NULL;
	if(!p_handle){
		return -1;
	}
	udsmutex_lock(p_handle);
	tmp = listen_msg_create_handle(&p_handle->uds_fp_listen_list,topic,src,cb);
	if(tmp == NULL){
		goto EXIT;
	}
EXIT:
	udsmutex_unlock(p_handle);
	return 0;
}

//解除监听消息：如果传入src，则需要同时匹配topic和src才能接触监听，src为NULL，则解除所有src的相关topic消息
int uds_topic_unlisten_ex(pt_uds_handle_arg p_handle,char *topic, char *src)
{
	int ret =  -1;
	if(!p_handle){
		return -1;
	}
	udsmutex_lock(p_handle);
	ret = listen_msg_free_handle(&p_handle->uds_fp_listen_list,topic,src);
	udsmutex_unlock(p_handle);

	return ret;
}


